﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Windows.Interop;
using System.Runtime.InteropServices;
using ViewOn.Kernel;
using ViewOn.UI.Dialog;
using ViewOn.UI.Control;
using ViewOnCore.Helper.WPF;
using ViewOnCore.HostContract.Media;
using ViewOnCore.HostContract.Tasks;
using ViewOnCore.HostContract;
using Un4seen.Bass.Misc;

namespace ViewOn.UI.VisualModeContainer.Normal
{

    /// <summary>
    /// The player-control is displayed at the top of the media-player and allow
    /// control of the current media.
    /// </summary>
    public partial class PlayerControl : UserControl
    {

        #region Variables

        //---- Spectrum
        private Visuals _visuals = new Visuals();
        private BitmapSource _bitmapSource;

        //---- Story boards
        private Storyboard _previousButtonShowStoryboard;
        private Storyboard _previousButtonHideStoryboard;

        private Storyboard _nextButtonShowStoryboard;
        private Storyboard _nextButtonHideStoryboard;

        //---- Tasks history
        private List<Task> TitleBarTextHistory = new List<Task>();

        #endregion

        #region Constructor

        public PlayerControl()
        {
            InitializeComponent();
			//---- Set up default state
            btnRepeat.IsChecked = Host.MediaService.IsRepeatMode;
			btnShuffle.IsChecked = Host.MediaService.IsShuffleMode;

			//---- Story boards
			_previousButtonShowStoryboard = (Storyboard)FindResource("previousButtonShowStoryboard");
			_previousButtonHideStoryboard = (Storyboard)FindResource("previousButtonHideStoryboard");

			_nextButtonShowStoryboard = (Storyboard)FindResource("nextButtonShowStoryboard");
			_nextButtonHideStoryboard = (Storyboard)FindResource("nextButtonHideStoryboard");

			_previousButtonHideStoryboard.Completed += new EventHandler(_previousButtonHideStoryboard_Completed);

			//---- Events
            Host.MediaService.MediaDeviceActivation += new EventHandler(MediaService_MediaDeviceActivation);
            Host.TaskStarted += new EventHandler(Host_TaskStarted);
            Host.TaskCompleted += new EventHandler(Host_TaskCompleted);
            Host.MediaService.PlayerStatusChanged += new EventHandler(MediaService_PlayerStatusChanged);
            Host.MediaService.MediaSelectionChanged += new EventHandler(MediaService_MediaSelectionChanged);
            Host.MediaService.MediaVisualRefresh += new EventHandler(MediaService_MediaVisualRefresh);
		}

		void _previousButtonHideStoryboard_Completed(object sender, EventArgs e)
		{
			btnPrevious.Visibility = Visibility.Collapsed;
		}

		#endregion

        #region VolumeChanged

        private void VolumeChanged(object sender, RoutedPropertyChangedEventArgs<double> args)
        {
            if (Host.MediaService.Volume != (int)slider.Value)
                Host.MediaService.Volume = (int)slider.Value;
        }

        #endregion

        #region PositionChanged

        private void PositionChanged(object sender, RoutedPropertyChangedEventArgs<double> args)
        {
            OnPositionChanged((float)sliPosition.Value);
        }

        #endregion

        #region MediaService : Events

        void MediaService_MediaDeviceActivation(object sender, EventArgs e)
        {
            Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                        (DelegateInvoker)delegate { OnMediaDeviceActivation(); });
        }

        void MediaService_PlayerStatusChanged(object sender, EventArgs e)
        {
            Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                        (DelegateInvoker)delegate { OnPlayerStatusChanged(); });
        }

        void MediaService_MediaSelectionChanged(object sender, EventArgs e)
        {
            Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                        (DelegateInvoker)delegate { OnMediaSelectionChanged(); });
        }

        void MediaService_MediaVisualRefresh(object sender, EventArgs e)
        {
            // "BeginInvoke" is important to avoid dead lock
            // of the Bass timer... there is an issue with this.
            Dispatcher.BeginInvoke(DispatcherPriority.Send,
                (DelegateInvoker)delegate { OnUpdateDisplay(); });
        }

        #endregion

        #region Buttons events

        public void btnRepeat_Click(object sender, RoutedEventArgs args)
        {
            Host.MediaService.RepeatInverse();
        }

        public void btnShuffle_Click(object sender, RoutedEventArgs args)
        {
            Host.MediaService.ShuffleInverse();
        }

        public void btnEqualizer_Click(object sender, RoutedEventArgs args)
        {
            EqualizerDialog dialog = new EqualizerDialog();
            dialog.ShowDialog(Host.ApplicationDesktop);
        }

        public void btnPrevious_Click(object sender, RoutedEventArgs args)
        {
            Host.MediaService.PreviousMedia();
        }

        public void btnNext_Click(object sender, RoutedEventArgs args)
        {
            Host.MediaService.NextMedia();
        }

        public void btnPlay_Click(object sender, RoutedEventArgs args)
        {
            Host.MediaService.Play();
        }

        public void btnMute_Click(object sender, RoutedEventArgs args)
        {
            Host.MediaService.MuteInverse();

            if (Host.MediaService.IsMuted)
            {
                patMuteOff.Visibility = Visibility.Visible;
                patMuteOn.Visibility = Visibility.Collapsed;
            }
            else
            {
                patMuteOff.Visibility = Visibility.Collapsed;
                patMuteOn.Visibility = Visibility.Visible;
            }
        }

        #endregion

        #region OnPositionChanged

        public void OnPositionChanged(float position)
        {
            Host.MediaService.Position = position;
        }

        #endregion

        #region OnMediaDeviceActivation

        public void OnMediaDeviceActivation()
        {
            //---- Next,Previous buttons
            if (Host.MediaService.CurrentMediaDeviceFeature.IsNextPreviousSupported)
            {
                //---- Show

                // already visible
                if (btnPrevious.Opacity > 0)
                    return;

                btnPrevious.Visibility = Visibility.Visible;
                _previousButtonShowStoryboard.Begin(this);
                _nextButtonShowStoryboard.Begin(this);
            }
            else
            {
                //---- Hide

                // already invisible
                if (btnPrevious.Opacity < 1)
                    return;

                _previousButtonHideStoryboard.Begin(this);
                _nextButtonHideStoryboard.Begin(this);
            }

            //---- Repeat, shuffle buttons
            if (Host.MediaService.CurrentMediaDeviceFeatureItem != null)
            {
                btnRepeat.IsEnabled = Host.MediaService.CurrentMediaDeviceFeatureItem.IsRepeatSupported;
                btnShuffle.IsEnabled = Host.MediaService.CurrentMediaDeviceFeatureItem.IsShuffleSupported;
            }
            else
            {
                btnRepeat.IsEnabled = Host.MediaService.CurrentMediaDeviceFeature.IsRepeatSupported;
                btnShuffle.IsEnabled = Host.MediaService.CurrentMediaDeviceFeature.IsShuffleSupported;
            }
        }

        #endregion

        #region OnVolumeChanged

        public void OnVolumeChanged()
        {
            slider.Value = Host.MediaService.Volume;
        }

        #endregion

        #region OnPlayerStatusChanged

        public void OnPlayerStatusChanged()
        {
            if (Host.MediaService.MediaPlayStatus == MediaPlayStatus.Play)
            {
                patPause.Visibility = Visibility.Visible;
                patPlay.Visibility = Visibility.Collapsed;
            }
            else
            {
                patPause.Visibility = Visibility.Collapsed;
                patPlay.Visibility = Visibility.Visible;
            }
        }

        #endregion

        #region OnMediaSelectionChanged

        public void OnMediaSelectionChanged()
        {
            UpdateControls();

            if (Host.MediaService.CurrentMedia == null)
            {
                txtTitle.Text = "-";
                txtDuration.Text = "-";
                return;
            }

            txtTitle.Text = Host.MediaService.CurrentMedia.Title;
            Music music = Host.MediaService.CurrentMedia as Music;
            if (music != null)
                txtDuration.Text = string.Format("- / {0:00}:{1:00}", (int)music.Duration.Minutes, (int)music.Duration.Seconds);
            else
                txtDuration.Text = "- / -";
        }

        void UpdateControls()
        {
            //if (!Host.MediaService.CurrentMediaDevice.IsNextPreviousSupported)
            //{
            //    btnPrevious.Visibility = Visibility.Hidden;
            //    btnPlay.Visibility = Visibility.Collapsed;
            //    btnNext.Visibility = Visibility.Hidden;
            //    sliPosition.Visibility = Visibility.Hidden;
            //    txtDuration.Visibility = Visibility.Hidden;
            //    return;
            //}

            //btnPrevious.Visibility = Visibility.Visible;
            //btnPlay.Visibility = Visibility.Visible;
            //btnNext.Visibility = Visibility.Visible;
            //sliPosition.Visibility = Visibility.Visible;
            //txtDuration.Visibility = Visibility.Visible;
        }

        #endregion

        #region OnMediaDataCannotConnect

        public void OnMediaDataCannotConnect()
        {
        }

        #endregion

        #region Host Task : waiting / stoping

        void Host_TaskStarted(object sender, EventArgs e)
        {
            // Keep the history of the tasks
            lock (TitleBarTextHistory)
                TitleBarTextHistory.Add(sender as Task);

            Dispatcher.BeginInvoke(DispatcherPriority.Send,
                (DelegateInvoker)delegate { AsyncStartWaiting(sender as Task); });
        }

        void Host_TaskCompleted(object sender, EventArgs e)
        {
            // Remove from the history
            lock (TitleBarTextHistory)
                TitleBarTextHistory.Remove(sender as Task);

            Dispatcher.BeginInvoke(DispatcherPriority.Send,
                (DelegateInvoker)delegate { AsyncStopWaiting(sender as Task); });
        }

        private void AsyncStartWaiting(Task startedTask)
        {
            OnStartWaiting(startedTask);
        }

        private void AsyncStopWaiting(Task completedTask)
        {
            OnStopWaiting(completedTask);
        }

        public void OnStartWaiting(Task startedTask)
        {
            if (spiRadio.IsRunning)
                return;

            griInformations.Visibility = Visibility.Hidden;
            spiRadio.Start();
            UpdateTextZone();
        }

        public void OnStopWaiting(Task completedTask)
        {
            if (Host.TasksCount > 0)
                return;

            spiRadio.Stop();
            griInformations.Visibility = Visibility.Visible;
            UpdateTextZone();
        }

        private void UpdateTextZone()
        {
            lock (TitleBarTextHistory)
                if (TitleBarTextHistory.Count > 0)
                {
                    txtTitle.Visibility = Visibility.Collapsed;
                    txtTitle2.Visibility = Visibility.Visible;
                    txtTitle2.Text = TitleBarTextHistory[TitleBarTextHistory.Count - 1].Message;
                }
                else
                {
                    txtTitle2.Text = "";
                    txtTitle.Visibility = Visibility.Visible;
                    txtTitle2.Visibility = Visibility.Collapsed;
                }
        }

        #endregion

        #region OnUpdateDisplay

        public void OnUpdateDisplay()
        {
            //---- Position
            sliPosition.ValueChanged -= new RoutedPropertyChangedEventHandler<double>(PositionChanged);
            try
            {
                sliPosition.Value = Host.MediaService.Position;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            sliPosition.ValueChanged += new RoutedPropertyChangedEventHandler<double>(PositionChanged);

            //---- Volume
            slider.ValueChanged -= new RoutedPropertyChangedEventHandler<double>(VolumeChanged);
            try
            {
                slider.Value = Host.MediaService.Volume;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            slider.ValueChanged += new RoutedPropertyChangedEventHandler<double>(VolumeChanged);

            //---- Time
            Music song = Host.MediaService.CurrentMedia as Music;
            if (song == null)
            {
                txtDuration.Text = "- : -";
            }
            else
            {
                TimeSpan totalTime = Host.MediaService.TotalTime;
                txtDuration.Text = string.Format("{0:00}:{1:00} / {2:00}:{3:00}", totalTime.Minutes, totalTime.Seconds, (int)song.Duration.Minutes, (int)song.Duration.Seconds);
            }

            //---- Draw the spectrum
            try
            {
                DrawSpectrum();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        #endregion

        #region DrawSpectrum

        private void DrawSpectrum()
        {
            if (this.Visibility != Visibility.Visible)
                return;

            Bitmap bitmap = CreateSpectrumBitmap();
            if (bitmap == null)
            {
                SpectrumImage.Source = null;
                SpectrumImageReflection.Source = null;
                return;
            }

            IntPtr hbitmap = bitmap.GetHbitmap();
            _bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(hbitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
            DeleteObject(hbitmap);
            bitmap.Dispose();
            SpectrumImage.Source = _bitmapSource;
            SpectrumImageReflection.Source = _bitmapSource;
        }

        //private int voicePrintIdx = 0;
        private Bitmap CreateSpectrumBitmap()
        {
            System.Drawing.Color color1 = System.Drawing.Color.FromArgb(Host.UIService.Color01.Color.R, Host.UIService.Color01.Color.G, Host.UIService.Color01.Color.B);
            System.Drawing.Color color3 = System.Drawing.Color.FromArgb(Host.UIService.Color02.Color.R, Host.UIService.Color02.Color.G, Host.UIService.Color02.Color.B);
            color1 = color3;
            System.Drawing.Color color2 = System.Drawing.Color.FromArgb(Host.UIService.Color03.Color.R, Host.UIService.Color03.Color.G, Host.UIService.Color03.Color.B);

            int height = (int)SpectrumImage.Height;

            switch (Host.SettingsService.SpectrumId)
            {

                case 0: // normal spectrum (width = resolution)
                    return _visuals.CreateSpectrum(BassPlayer.Stream, (int)SpectrumImage.Width, (int)height, color1, color2, System.Drawing.Color.Empty, false, false, false);

                case 1: // normal spectrum (full resolution)
                    return _visuals.CreateSpectrum(BassPlayer.Stream, (int)SpectrumImage.Width, (int)height, color1, color2, System.Drawing.Color.Empty, false, true, true);

                case 2: // line spectrum (width = resolution)
                    return _visuals.CreateSpectrumLine(BassPlayer.Stream, (int)SpectrumImage.Width, (int)height, color1, color2, System.Drawing.Color.Empty, 2, 2, false, false, false);

                case 3: // line spectrum (full resolution)
                    return _visuals.CreateSpectrumLine(BassPlayer.Stream, (int)SpectrumImage.Width, (int)height, color1, color2, System.Drawing.Color.Empty, 16, 4, false, true, true);

                //case 4: // ellipse spectrum (width = resolution)
                //    return _visuals.CreateSpectrumEllipse(BassPlayer.Stream, (int)SpectrumImage.Width, (int)height, color1, color2, System.Drawing.Color.Empty, 1, 2, false, false, false);

                case 4: // ellipse spectrum (full resolution)
                    return _visuals.CreateSpectrumEllipse(BassPlayer.Stream, (int)SpectrumImage.Width, (int)height, color1, color2, System.Drawing.Color.Empty, 2, 4, false, true, true);

                case 5: // dot spectrum (width = resolution)
                    return _visuals.CreateSpectrumDot(BassPlayer.Stream, (int)SpectrumImage.Width, (int)height, color1, color2, System.Drawing.Color.Empty, 1, 0, false, false, false);

                case 6: // dot spectrum (full resolution)
                    return _visuals.CreateSpectrumDot(BassPlayer.Stream, (int)SpectrumImage.Width, (int)height, color1, color2, System.Drawing.Color.Empty, 2, 1, false, false, true);

                case 7: // peak spectrum (width = resolution)
                    return _visuals.CreateSpectrumLinePeak(BassPlayer.Stream, (int)SpectrumImage.Width, (int)height, color1, color2, color3, System.Drawing.Color.Empty, 3, 1, 2, 10, false, false, false);
                //return _vis.CreateSpectrumLinePeak(BassPlayer.Stream, (int)SpectrumImage.Width, (int)height, System.Drawing.Color.SeaGreen, System.Drawing.Color.LightGreen, System.Drawing.Color.Orange, System.Drawing.Color.Black, 2, 1, 2, 10, false, false, false);

                case 8: // wave spectrum (width = resolution)
                    return _visuals.CreateSpectrumWave(BassPlayer.Stream, (int)SpectrumImage.Width, (int)height, color1, color2, System.Drawing.Color.Empty, 1, false, false, false);

                case 9: // dancing beans spectrum (width = resolution)
                    return _visuals.CreateSpectrumBean(BassPlayer.Stream, (int)SpectrumImage.Width, (int)height, color1, color2, System.Drawing.Color.Empty, 4, false, false, true);

                case 10: // dancing text spectrum (width = resolution)
                    return _visuals.CreateSpectrumText(BassPlayer.Stream, (int)SpectrumImage.Width, (int)height, color1, color2, System.Drawing.Color.Empty, "----------", false, false, true);

                case 11:
                    return _visuals.CreateWaveForm(BassPlayer.Stream, (int)SpectrumImage.Width, (int)height, color1, color2, color3, System.Drawing.Color.Empty, 1, true, false, true);

                case 12:
                    return null;

                //case 9:// peak spectrum (full resolution)
                //    return _visuals.CreateSpectrumLinePeak(BassPlayer.Stream, (int)SpectrumImage.Width, (int)height, color1, color2, color3, System.Drawing.Color.Empty, 23, 5, 3, 5, false, true, true);

                // frequency detection
                //case 14:
                //    float amp = _vis.DetectFrequency(BassPlayer.Stream, 10, 500, true);
                //    if (amp > 0.3)
                //        SpectrumImage.BackColor = System.Drawing.Color.Red;
                //    else
                //        SpectrumImage.BackColor = System.Drawing.Color.Black;
                //    break;
                //// 3D voice print
                //case 15:
                //    // we need to draw directly directly on the picture box...
                //    // normally you would encapsulate this in your own custom control
                //    Graphics g = Graphics.FromHwnd(SpectrumImage.Handle);
                //    g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
                //    _vis.CreateSpectrum3DVoicePrint(BassPlayer.Stream, g, new Rectangle(0, 0, (int)SpectrumImage.Width, (int)height), System.Drawing.Color.Black, System.Drawing.Color.White, voicePrintIdx, false, false);
                //    g.Dispose();
                //    // next call will be at the next pos
                //    voicePrintIdx++;
                //    if (voicePrintIdx > SpectrumImage.Width - 1)
                //        voicePrintIdx = 0;
                //    break;
                // WaveForm
                //case 16:
                //	return _visuals.CreateWaveForm(BassPlayer.Stream, (int)SpectrumImage.Width, (int)height, color1, color2, color3, System.Drawing.Color.Empty, 1, true, false, true);
            }

            return null;
        }

        #endregion

        #region spectrum_MouseUp

        public void spectrum_MouseUp(object sender, MouseEventArgs args)
        {
            Host.SettingsService.SpectrumId++;
            if (Host.SettingsService.SpectrumId > 12)
                Host.SettingsService.SpectrumId = 0;
        }

        #endregion

        #region API

        [DllImport("gdi32.dll")]
        public static extern bool DeleteObject(IntPtr hObject);

        [DllImport("winmm.dll")]
        public static extern int waveOutGetVolume(IntPtr hwo, out uint dwVolume);

        #endregion

    }

}
